
package com.pascalwelsch.holocircularprogressbar;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

/**
 * HoloCircularProgressBar custom view.
 *
 * @author Pascal.Welsch
 * @version 1.3 (03.10.2014)
 * @since 05.03.2013
 */
public class HoloCircularProgressBar extends Component implements Component.DrawTask, Component.LayoutRefreshedListener {

    /**
     * TAG constant for logging
     */
    private static final String TAG = HoloCircularProgressBar.class.getSimpleName();

    /**
     * used to save the super state on configuration change
     */
    private static final String INSTANCE_STATE_SAVEDSTATE = "saved_state";

    /**
     * used to save the progress on configuration changes
     */
    private static final String INSTANCE_STATE_PROGRESS = "progress";

    /**
     * used to save the marker progress on configuration changes
     */
    private static final String INSTANCE_STATE_MARKER_PROGRESS = "marker_progress";

    /**
     * used to save the background color of the progress
     */
    private static final String INSTANCE_STATE_PROGRESS_BACKGROUND_COLOR
            = "progress_background_color";

    /**
     * used to save the color of the progress
     */
    private static final String INSTANCE_STATE_PROGRESS_COLOR = "progress_color";

    /**
     * used to save and restore the visibility of the thumb in this instance
     */
    private static final String INSTANCE_STATE_THUMB_VISIBLE = "thumb_visible";

    /**
     * used to save and restore the visibility of the marker in this instance
     */
    private static final String INSTANCE_STATE_MARKER_VISIBLE = "marker_visible";

    /**
     * The rectangle enclosing the circle.
     */
    private RectFloat mCircleBounds = new RectFloat();

    /**
     * the rect for the thumb square
     */
    private final RectFloat mSquareRect = new RectFloat();

    /**
     * the paint for the background.
     */
    private Paint mBackgroundColorPaint = new Paint();

    /**
     * The stroke width used to paint the circle.
     */
    private int mCircleStrokeWidth = 10;

    /**
     * The gravity of the view. Where should the Circle be drawn within the given bounds
     * <p>
     * {@link #computeInsets(int, int)}
     */
    private int mGravity = 1;

    /**
     * The Horizontal inset calcualted in {@link #computeInsets(int, int)} depends on {@link
     * #mGravity}*.
     */
    private int mHorizontalInset = 0;

    /**
     * true if not all properties are set. then the view isn't drawn and there are no errors in the
     * LayoutEditor
     */
    private boolean mIsInitializing = true;

    /**
     * flag if the marker should be visible
     */
    private boolean mIsMarkerEnabled = false;

    /**
     * indicates if the thumb is visible
     */
    private boolean mIsThumbEnabled = true;

    /**
     * The Marker color paint.
     */
    private Paint mMarkerColorPaint;

    /**
     * The Marker progress.
     */
    private float mMarkerProgress = 0.0f;

    /**
     * the overdraw is true if the progress is over 1.0.
     */
    private boolean mOverrdraw = false;

    /**
     * The current progress.
     */
    private float mProgress = 0.3f;

    /**
     * The color of the progress background.
     */
    private int mProgressBackgroundColor;

    /**
     * the color of the progress.
     */
    private int mProgressColor;

    /**
     * paint for the progress.
     */
    private Paint mProgressColorPaint;

    /**
     * Radius of the circle
     *
     * <p> Note: (Re)calculated in (int, int)}. </p>
     */
    private float mRadius;

    /**
     * The Thumb color paint.
     */
    private Paint mThumbColorPaint = new Paint();

    /**
     * The Thumb pos x.
     * <p>
     * Care. the position is not the position of the rotated thumb. The position is only calculated
     * in
     */
    private float mThumbPosX;

    /**
     * The Thumb pos y.
     * <p>
     * Care. the position is not the position of the rotated thumb. The position is only calculated
     * in
     */
    private float mThumbPosY;

    /**
     * The pointer width (in pixels).
     */
    private int mThumbRadius = 20;

    /**
     * The Translation offset x which gives us the ability to use our own coordinates system.
     */
    private float mTranslationOffsetX;

    /**
     * The Translation offset y which gives us the ability to use our own coordinates system.
     */
    private float mTranslationOffsetY;

    /**
     * The Vertical inset calcualted in {@link #computeInsets(int, int)} depends on {@link
     * #mGravity}*..
     */
    private int mVerticalInset = 0;

    /**
     * Upgrade SDK 5 to adapt to the onRefresh and onDraw sequences.
     */
    private boolean mIsOnRefreshed = false;

    /**
     * Instantiates a new holo circular progress bar.
     *
     * @param context the context
     */
    public HoloCircularProgressBar(final Context context) {
        this(context, null);
    }

    /**
     * Instantiates a new holo circular progress bar.
     *
     * @param context the context
     * @param attrs   the attrs
     */
    public HoloCircularProgressBar(final Context context, final AttrSet attrs) {
        this(context, attrs, null);
    }

    /**
     * * The constant HOLOCIRCULARPROGRESSBAR_STROKE_WIDTH
     */
    private final static String HOLOCIRCULARPROGRESSBAR_STROKE_WIDTH = "stroke_width";
    /**
     * * The constant HOLOCIRCULARPROGRESSBAR_PROGRESS
     */
    private final static String HOLOCIRCULARPROGRESSBAR_PROGRESS = "holo_progress";
    /**
     * * The constant HOLOCIRCULARPROGRESSBAR_MARKER_PROGRESS
     */
    private final static String HOLOCIRCULARPROGRESSBAR_MARKER_PROGRESS = "marker_progress";
    /**
     * * The constant HOLOCIRCULARPROGRESSBAR_PROGRESS_COLOR
     */
    private final static String HOLOCIRCULARPROGRESSBAR_PROGRESS_COLOR = "holo_progress_color";
    /**
     * * The constant HOLOCIRCULARPROGRESSBAR_PROGRESS_BACKGROUND_COLOR
     */
    private final static String HOLOCIRCULARPROGRESSBAR_PROGRESS_BACKGROUND_COLOR = "progress_background_color";
    /**
     * * The constant HOLOCIRCULARPROGRESSBAR_THUMB_VISIBLE
     */
    private final static String HOLOCIRCULARPROGRESSBAR_THUMB_VISIBLE = "thumb_visible";
    /**
     * * The constant HOLOCIRCULARPROGRESSBAR_MARKER_VISIBLE
     */
    private final static String HOLOCIRCULARPROGRESSBAR_MARKER_VISIBLE = "marker_visible";

    /**
     * Instantiates a new holo circular progress bar.
     *
     * @param context  the context
     * @param attrs    the attrs
     * @param defStyle the def style
     */
    public HoloCircularProgressBar(final Context context, final AttrSet attrs,
                final String defStyle) {
        super(context, attrs, defStyle);

        // load the styled attributes and set their properties


        setProgressColor(AttrUtils.getColorFromAttr(attrs, HOLOCIRCULARPROGRESSBAR_PROGRESS_COLOR, Color.CYAN.getValue()));
        setProgressBackgroundColor(AttrUtils.getColorFromAttr(attrs, HOLOCIRCULARPROGRESSBAR_PROGRESS_BACKGROUND_COLOR, Color.GREEN.getValue()));
        setProgress(AttrUtils.getFloatFromAttr(attrs, HOLOCIRCULARPROGRESSBAR_PROGRESS, 0.0f));
        setMarkerProgress(AttrUtils.getFloatFromAttr(attrs, HOLOCIRCULARPROGRESSBAR_MARKER_PROGRESS, 0.0f));
        setWheelSize(AttrUtils.getDimensionFromAttr(attrs, HOLOCIRCULARPROGRESSBAR_STROKE_WIDTH, 10));
        setThumbEnabled(AttrUtils.getBooleanFromAttr(attrs, HOLOCIRCULARPROGRESSBAR_THUMB_VISIBLE, true));
        setMarkerEnabled(AttrUtils.getBooleanFromAttr(attrs, HOLOCIRCULARPROGRESSBAR_MARKER_VISIBLE, true));

        mThumbRadius = mCircleStrokeWidth * 2;

        updateBackgroundColor();

        updateMarkerColor();

        updateProgressColor();

        // the view has now all properties and can be drawn
        mIsInitializing = false;
        addDrawTask(this);
        setLayoutRefreshedListener(this);

    }

    /**
     * On refreshed *
     *
     * @param component component
     */
    @Override
    public void onRefreshed(Component component) {
        final int height =
                getHeight();
        final int width =
                getWidth();

        final int diameter;
        // Default
        diameter = Math.min(width, height);
        computeInsets(width - diameter, height - diameter);


        final float halfWidth = (float) (diameter * 0.5d);

        // width of the drawed circle (+ the drawedThumb)
        final float drawedWith;
        if (isThumbEnabled()) {
            drawedWith = mThumbRadius * (5f / 6f);
        } else if (isMarkerEnabled()) {
            drawedWith = mCircleStrokeWidth * 1.4f;
        } else {
            drawedWith = mCircleStrokeWidth / 2f;
        }

        // -0.5f for pixel perfect fit inside the viewbounds
        mRadius = halfWidth - drawedWith - 0.5f;

        mCircleBounds = new RectFloat(-mRadius, -mRadius, mRadius, mRadius);

        mThumbPosX = (float) (mRadius * Math.cos(0));
        mThumbPosY = (float) (mRadius * Math.sin(0));

        mTranslationOffsetX = halfWidth + mHorizontalInset;
        mTranslationOffsetY = halfWidth + mVerticalInset;

    }

    /**
     * On draw *
     *
     * @param component component
     * @param canvas    canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (!mIsOnRefreshed) {
            onRefreshed(component);
            mIsOnRefreshed = true;
        }
        // All of our positions are using our internal coordinate system.
        // Instead of translating
        // them we let Canvas do the work for us.
        canvas.translate(mTranslationOffsetX, mTranslationOffsetY);

        final float progressRotation = getCurrentRotation();

        // draw the background
        if (!mOverrdraw) {
            canvas.drawArc(mCircleBounds, new Arc(270, -(360 - progressRotation), false),
                    mBackgroundColorPaint);
        }

        // draw the progress or a full circle if overdraw is true
        canvas.drawArc(mCircleBounds, new Arc(270, mOverrdraw ? 360 : progressRotation, false),
                mProgressColorPaint);

        // draw the marker at the correct rotated position
        if (mIsMarkerEnabled) {
            final float markerRotation = getMarkerRotation();

            canvas.save();
            /**
             * 旋转
             */
            canvas.rotate(markerRotation - 90, 0, 0);
            canvas.drawLine(new Point((float) (mThumbPosX + mThumbRadius / 2 * 1.4), mThumbPosY),
                    new Point((float) (mThumbPosX - mThumbRadius / 2 * 1.4), mThumbPosY), mMarkerColorPaint);
            canvas.restore();
        }

        if (isThumbEnabled()) {
            // draw the thumb square at the correct rotated position
            canvas.save();
            canvas.rotate(progressRotation - 90, 0, 0);
            // rotate the square by 45 degrees
            canvas.rotate(45, mThumbPosX, mThumbPosY);
            mSquareRect.left = mThumbPosX - mThumbRadius / 3;
            mSquareRect.right = mThumbPosX + mThumbRadius / 3;
            mSquareRect.top = mThumbPosY - mThumbRadius / 3;
            mSquareRect.bottom = mThumbPosY + mThumbRadius / 3;
            canvas.drawRect(mSquareRect, mThumbColorPaint);
            canvas.restore();
        }
    }


    /**
     * Get circle stroke width int
     *
     * @return the int
     */
    public int getCircleStrokeWidth() {
        return mCircleStrokeWidth;
    }

    /**
     * similar to {@link #getProgress}
     *
     * @return the float
     */
    public float getMarkerProgress() {
        return mMarkerProgress;
    }

    /**
     * gives the current progress of the ProgressBar. Value between 0..1 if you set the progress to
     * >1 you'll get progress % 1 as return value
     *
     * @return the progress
     */
    public float getProgress() {
        return mProgress;
    }

    /**
     * Gets the progress color.
     *
     * @return the progress color
     */
    public int getProgressColor() {
        return mProgressColor;
    }

    /**
     * Is marker enabled boolean
     *
     * @return true if the marker is visible
     */
    public boolean isMarkerEnabled() {
        return mIsMarkerEnabled;
    }

    /**
     * Is thumb enabled boolean
     *
     * @return true if the marker is visible
     */
    public boolean isThumbEnabled() {
        return mIsThumbEnabled;
    }

    /**
     * Sets the marker enabled.
     *
     * @param enabled the new marker enabled
     */
    public void setMarkerEnabled(final boolean enabled) {
        mIsMarkerEnabled = enabled;
    }

    /**
     * Sets the marker progress.
     *
     * @param progress the new marker progress
     */
    public void setMarkerProgress(final float progress) {
        mIsMarkerEnabled = true;
        mMarkerProgress = progress;
    }

    /**
     * Sets the progress.
     *
     * @param progress the new progress
     */
    public void setProgress(final float progress) {
        if (progress == mProgress) {
            return;
        }

        if (progress == 1) {
            mOverrdraw = false;
            mProgress = 1;
        } else {

            if (progress >= 1) {
                mOverrdraw = true;
            } else {
                mOverrdraw = false;
            }

            mProgress = progress % 1.0f;
        }

        if (!mIsInitializing) {
            invalidate();
        }
    }

    /**
     * Sets the progress background color.
     *
     * @param color the new progress background color
     */
    public void setProgressBackgroundColor(final int color) {
        mProgressBackgroundColor = color;

        updateMarkerColor();
        updateBackgroundColor();
    }

    /**
     * Sets the progress color.
     *
     * @param color the new progress color
     */
    public void setProgressColor(final int color) {
        mProgressColor = color;

        updateProgressColor();
    }

    /**
     * shows or hides the thumb of the progress bar
     *
     * @param enabled true to show the thumb
     */
    public void setThumbEnabled(final boolean enabled) {
        mIsThumbEnabled = enabled;
    }

    /**
     * Sets the wheel size.
     *
     * @param dimension the new wheel size
     */
    public void setWheelSize(final int dimension) {
        mCircleStrokeWidth = dimension;

        // update the paints
        updateBackgroundColor();
        updateMarkerColor();
        updateProgressColor();
    }

    /**
     * Compute insets.
     *
     * <pre>
     *  ______________________
     * |_________dx/2_________|
     * |......| /'''''\|......|
     * |-dx/2-|| View ||-dx/2-|
     * |______| \_____/|______|
     * |________ dx/2_________|
     * </pre>
     *
     * @param dx the dx the horizontal unfilled space
     * @param dy the dy the horizontal unfilled space
     */
    private void computeInsets(final int dx, final int dy) {

    }

    /**
     * Gets the current rotation.
     *
     * @return the current rotation
     */
    private float getCurrentRotation() {
        return 360 * mProgress;
    }

    /**
     * Gets the marker rotation.
     *
     * @return the marker rotation
     */
    private float getMarkerRotation() {
        return 360 * mMarkerProgress;
    }

    /**
     * updates the paint of the background
     */
    private void updateBackgroundColor() {
        mBackgroundColorPaint = new Paint();
        mBackgroundColorPaint.setAntiAlias(true);
        mBackgroundColorPaint.setColor(new Color(mProgressBackgroundColor));
        mBackgroundColorPaint.setStyle(Paint.Style.STROKE_STYLE);
        mBackgroundColorPaint.setStrokeWidth(mCircleStrokeWidth);

        invalidate();
    }

    /**
     * updates the paint of the marker
     */
    private void updateMarkerColor() {
        mMarkerColorPaint = new Paint();
        mMarkerColorPaint.setAntiAlias(true);
        mMarkerColorPaint.setColor(new Color(mProgressBackgroundColor));
        mMarkerColorPaint.setStyle(Paint.Style.STROKE_STYLE);
        mMarkerColorPaint.setStrokeWidth(mCircleStrokeWidth / 2);

        invalidate();
    }

    /**
     * updates the paint of the progress and the thumb to give them a new visual style
     */
    private void updateProgressColor() {
        mProgressColorPaint = new Paint();
        mProgressColorPaint.setAntiAlias(true);
        mProgressColorPaint.setColor(new Color(mProgressColor));
        mProgressColorPaint.setStyle(Paint.Style.STROKE_STYLE);
        mProgressColorPaint.setStrokeWidth(mCircleStrokeWidth);

        mThumbColorPaint = new Paint();
        mThumbColorPaint.setAntiAlias(true);
        mThumbColorPaint.setColor(new Color(mProgressColor));
        mThumbColorPaint.setStyle(Paint.Style.FILLANDSTROKE_STYLE);
        mThumbColorPaint.setStrokeWidth(mCircleStrokeWidth);

        invalidate();
    }


}
